package top.someapp.applib.fn;

import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @author zwz
 * Created on 2020-03-31
 */
public class Some<E> {

    private final Collection<E> collection;
    Fn.Filter<E> nonNullFilter = new Fn.Filter<E>() {
        @Override
        public boolean test(@NonNull E e) {
            return e != null;
        }
    };

    public Some(@NonNull E... elements) {
        this.collection = Arrays.asList(elements);
    }

    public Some(@NonNull Collection<E> collection) {
        this.collection = collection;
    }

    public Collection<E> all() {
        return Collections.unmodifiableCollection(collection);
    }

    public void each(@NonNull Fn.VoidCallback<E> callback) {
        for (E el : collection) {
            callback.call(el);
        }
    }

    public Collection<E> include(@NonNull Fn.Filter<E> filter) {
        List<E> list = new ArrayList<>(collection.size());
        for (E el : collection) {
            if (filter.test(el)) {
                list.add(el);
            }
        }
        return list;
    }

    public Collection<E> exclude(@NonNull Fn.Filter<E> filter) {
        List<E> list = new ArrayList<>(collection.size());
        for (E el : collection) {
            if (!filter.test(el)) {
                list.add(el);
            }
        }
        return list;
    }

    private E doFilter(Fn.Filter<E> filter, boolean breakIfFound) {
        E target = null;
        if (breakIfFound) {
            for (E el : collection) {
                if (filter.test(el)) {
                    target = el;
                    break;
                }
            }
        } else {
            for (E el : collection) {
                if (filter.test(el)) {
                    target = el;
                }
            }
        }
        return target;
    }

    public E first(@NonNull Fn.Filter<E> filter) {
        return doFilter(filter, true);
    }

    public E first(@NonNull Fn.Filter<E> filter, E valueIfNull) {
        E target = doFilter(filter, true);
        return target == null ? valueIfNull : target;
    }

    public E firstNonNull() {
        return doFilter(nonNullFilter, true);
    }

    public E last(@NonNull Fn.Filter<E> filter) {
        return doFilter(filter, false);
    }

    public E last(@NonNull Fn.Filter<E> filter, E valueIfNull) {
        E target = doFilter(filter, false);
        return target == null ? valueIfNull : target;
    }

    /// -------------------------------------------------------------------------------------------- chain operations
    public Some<E> includeSome(@NonNull Fn.Filter<E> filter) {
        return new Some<>(include(filter));
    }

    public Some<E> sort(@NonNull Comparator<E> comparator) {
        List<E> list = new ArrayList<>(collection);
        Collections.sort(list, comparator);
        return new Some<>(list);
    }

    public <T> Some<T> castToSome(@NonNull Fn.Castor<E, T> castor) {
        List<T> toList = new ArrayList<>();
        for (E el : collection) {
            toList.add(castor.cast(el));
        }
        return new Some<>(toList);
    }
    /// ~-------------------------------------------------------------------------------------------- chain operations

}
